/**
 * Licensed under the Apache License Version 2.0.
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package net.interknit.knitter.cg.view;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import net.interknit.knitter.cg.CGMain;
import net.interknit.knitter.cg.model.JavaMethod;
import net.interknit.knitter.cg.model.JavaType;
import net.interknit.knitter.cg.template.Template;
import net.interknit.knitter.cg.util.ReflectionHelper;
import net.interknit.knitter.cg.util.Utils;

/**
 *
 */
public abstract class JavaTypeView extends Template
{
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
	private static String[] defaultPlaceholders = new String[] {
		"TIMESTAMP",
		"AUTHOR",
		"COMMENTS",
		"ADAPTER_NAME",
		"ADAPTER_CLASS_NAME",
		"ADAPTER_DECLARATION",
		"STATE_DECLARATION",
		"STATE_INSTANTIATION",
		"VALUE_DECLARATION",
		"VALUE_INSTANTIATION",
		"FIELDS_XML",
	};

	private final JavaType javaType;

	public JavaTypeView(JavaType javaType)
	{
		this.javaType = javaType;
	}

	public final JavaType getJavaType()
	{
		return javaType;
	}

	@Override
	public final String getJavaClassName()
	{
		return javaType.getJavaTypeId(false);
	}

	@Override
	public String getJavaPackageName()
	{
		return javaType.getJavaTypePackageName();
	}

	@Override
	public final String[] getPlaceholders()
	{
		List<String> list = new ArrayList<String>();

		for(String ph : defaultPlaceholders)
			list.add(ph);
		for(String ph : getAdditionalPlaceholders())
			if(! list.contains(ph)) list.add(ph);

		return list.toArray(new String[list.size()]);
	}

	@Override
	public boolean skipSourceGeneration()
	{
		return ! javaType.isReferenced();
	}

	protected void assertCondition(boolean condition, String fromMethod)
	{
		if(! condition)
			throw new RuntimeException("Type mismatch, " + ReflectionHelper.getClassName(getClass()) + "." + fromMethod + " " +
					"called on JavaType: " + javaType.getJavaTypeId());
	}

	public String TIMESTAMP()
	{
		return sdf.format(new Date());
	}

	public String AUTHOR()
	{
		return CGMain.getInstance().getCGConfig().getAuthor();
	}

	public String COMMENTS()
	{
		String comments = CGMain.getInstance().getCGConfig().getTypeComments(
				ReflectionHelper.getClassName(javaType.getTypeClass()));

		if(comments != null && ! "".equals(comments))
			return Utils.formatJavadoc(comments, 0, false);
		else
			return "File Name: " + getJavaClassName() + ".java";
	}

	public String ADAPTER_NAME()
	{
		String adapterName = CGMain.getInstance().getCGConfig().getTypeRestName(
				ReflectionHelper.getClassName(javaType.getTypeClass()));

		adapterName = adapterName != null ? adapterName : javaType.getJavaTypeId();

		return Utils.createValidVariableName(adapterName);
	}

	public String ADAPTER_CLASS_NAME()
	{
		return javaType.getJavaTypeId(false);
	}

	public String ADAPTER_DECLARATION()
	{
		String adapterDeclaration = null;

		// if code-gen is configured to use fully qualified type names
		// then the package name is already part of the adapter declaration
		if(CGMain.getInstance().useQualifiedTypeNames())
		{
			adapterDeclaration = javaType.getJavaTypeId();
		}
		// if code-gen is configured to use short (unqualified) names
		// then the package name needs to be added to create the adapter declaration
		else
		{
			String jtpm = javaType.getJavaTypePackageName();
			adapterDeclaration = (jtpm != null && ! jtpm.isEmpty() ? (jtpm + ".") : "") + javaType.getJavaTypeId();
		}

		return adapterDeclaration;
	}

	public String ADAPTER_INSTANTIATION()
	{
		String adapterDeclaration = null;

		// if code-gen is configured to use fully qualified type names
		// then the package name is already part of the adapter declaration
		if(CGMain.getInstance().useQualifiedTypeNames())
		{
			adapterDeclaration = javaType.getJavaTypeId();
		}
		// if code-gen is configured to use short (unqualified) names
		// then the package name needs to be added to create the adapter declaration
		else
		{
			String jtpm = javaType.getJavaTypePackageName();
			adapterDeclaration = (jtpm != null && ! jtpm.isEmpty() ? (jtpm + ".") : "") + javaType.getJavaTypeId();
		}

		return adapterDeclaration;
	}

	public String STATE_DECLARATION()
	{
		return javaType.getJavaTypeDeclaration();
	}

	public String STATE_INSTANTIATION()
	{
		return javaType.getConcreteJavaType().getJavaTypeDeclaration();
	}

	public String VALUE_DECLARATION()
	{
		return javaType.getJavaTypeDeclaration();
	}

	public String VALUE_INSTANTIATION()
	{
		return javaType.getConcreteJavaType().getJavaTypeDeclaration();
	}

	public String UNWRAP_CLAUSE()
	{
		return ".unwrap()";
	}

	public String FIELDS_XML()
	{
		StringBuilder sb = new StringBuilder();

		for(JavaMethod javaMethod : javaType.getJavaMethods())
		{
			boolean accessorMethod = javaMethod.isAccessor();

			if(accessorMethod)
			{
				String name = javaMethod.getName();

				if(
						(name.startsWith("get") && name.length() > 3) ||
						(name.startsWith("is") && name.length() > 2)
				)
				{
					JavaType returnJavaType = javaMethod.getReturnJavaType();

					String fieldName = Utils.derivePropertyName(name);

					String fieldType = JavaTypeViewFactory.create(returnJavaType).ADAPTER_NAME();

					if(returnJavaType.getInspector().isPrimitiveType() || returnJavaType.getInspector().isBasicType())
					{
						fieldType = ReflectionHelper.getStandardTypeName(fieldType);
					}

					sb.append("<field name=\"" + fieldName + "\" type=\"" + fieldType + "\" />");
					sb.append("\n");
				}
			}
		}

		return sb.toString();
	}

	protected String[] getAdditionalPlaceholders()
	{
		return new String[0];
	}
}
